// HSS.Data.DataAccessConnection.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       DataAccessConnection.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/24/2010
// ----------------------------------------------------------------------------
namespace HSS.Data
{
	#region Using Directives
	using System;
	using System.Data.SqlClient;
	using System.Collections.Generic;
	using System.Data.Common;
	using System.ComponentModel;
	using System.Collections;
	using System.Threading;
	using System.Resources;
	using System.Globalization;
	using System.Runtime.Serialization;
	#endregion

	#region DataAccessConnection
	/// <summary>
	/// Contains a named SqlConnectionString, and provides access
	/// to the common elements, and can be used for serialization
	/// or transport.
	/// </summary>
	[Serializable]
	public sealed class DataAccessConnection : ISecuredConfigurationElement, ISerializable
	{
		#region Constants
		/// <summary>
		/// A black circle (0x25CF) which can be used as a Mask for passwords
		/// </summary>
		public const Char PwdChar = (char)0x25CF;
		private const int commandTimeoutDefault = 60;
		#endregion

		#region Fields
		private readonly SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
		#endregion

		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		/// <remarks>Required for Serialization</remarks>
		public DataAccessConnection()
			: this((string)null)
		{
		}
		/// <summary>
		/// Creates a new DataAccessConnection using parameters specified
		/// </summary>
		/// <param name="name">The unique friendly name for this database configuration</param>
		/// <param name="integratedSecurity">Indicate whether or not to use Integrated Security</param>
		/// <param name="userId">The user id to access the database</param>
		/// <param name="password">The user password to access the database</param>
		/// <param name="datasource">The Name, UNC, URL or IP Address of the Datasource</param>
		/// <param name="initialCatalog">If the DataAccessType is SqlAcess, the initial catalog for the connection</param>
		/// <param name="applicationName">The application name for this ConnectionString</param>
		/// <param name="workstationId">The workstationID for this ConnectionString</param>
		/// <param name="connectionTimeout">The allowable amount of time to create a connection to the database</param>
		/// <param name="commandTimeout">The allowable amount of time to execute a command against the database</param>
		public DataAccessConnection(string name, bool integratedSecurity, string userId, string password, string datasource, string initialCatalog, string applicationName, string workstationId, int connectionTimeout, int commandTimeout)
		{
			this.Name = name;
			this.CommandTimeout = commandTimeoutDefault;

			if (integratedSecurity)
				this.IntegratedSecurity = integratedSecurity;
			else
			{
				if (!string.IsNullOrEmpty(userId))
					this.UserID = userId;
				if (!string.IsNullOrEmpty(password))
					this.Password = password;
			}

			if (!string.IsNullOrEmpty(datasource))
				this.DataSource = datasource;

			if (!string.IsNullOrEmpty(initialCatalog))
				this.InitialCatalog = initialCatalog;

			if (!string.IsNullOrEmpty(applicationName) && !string.Equals(applicationName, this.ApplicationName, StringComparison.CurrentCulture))
				this.ApplicationName = applicationName;

			if (!string.IsNullOrEmpty(workstationId))
				this.WorkstationID = workstationId;

			if (connectionTimeout > 0 && connectionTimeout != this.ConnectTimeout)
				this.ConnectTimeout = connectionTimeout;

			if (commandTimeout > 0)
				this.CommandTimeout = commandTimeout;
		}
		/// <summary>
		/// Creates a new <see cref="DataAccessConnection"/> using the connection string specified.
		/// </summary>
		/// <param name="name">The name to reference this connection by.</param>
		/// <param name="connectionString">The connection string to create this instance from.</param>
		public DataAccessConnection(string name, string connectionString)
		{
			Guard.ArgumentNotNullOrEmpty(name, "name");
			this.Name = name;
			this.CommandTimeout = commandTimeoutDefault;
			this.ConnectionString = connectionString;
		}
		/// <summary>
		/// Creates a new <see cref="DataAccessConnection"/> using the connection string specified.
		/// </summary>
		/// <param name="connectionString">The connection string to create this instance from.</param>
		public DataAccessConnection(string connectionString)
		{
			this.Name = string.Empty;
			this.CommandTimeout = commandTimeoutDefault;
			this.ConnectionString = connectionString;
		}
		/// <summary>
		/// Creates a new <see cref="DataAccessConnection"/> using the <see cref="DataAccessConnection"/> specified
		/// </summary>
		/// <param name="dataAccessConnection">An existing <see cref="DataAccessConnection"/> to use</param>
		public DataAccessConnection(DataAccessConnection dataAccessConnection)
		{
			Guard.ArgumentNotNull(dataAccessConnection, "dataAccessConnection");
			this.Name = dataAccessConnection.Name;
			this.CommandTimeout = dataAccessConnection.CommandTimeout;
			this.ConnectionString = dataAccessConnection.ConnectionString;
		}
		/// <summary>
		/// Construct this instance from the SerializationInfo.
		/// </summary>
		/// <param name="info">The SerializationInfo to construct from.</param>
		/// <param name="context">The StreamingContext.</param>
		public DataAccessConnection(SerializationInfo info, StreamingContext context)
		{
			Guard.ArgumentNotNull(info, "info");

			try { this.Name = info.GetString("Name"); }
			catch { this.Name = string.Empty; }

			try { this.CommandTimeout = info.GetInt32("CommandTimeout"); }
			catch { this.CommandTimeout = commandTimeoutDefault; }

			try { this.ConnectionString = info.GetString("ConnectionString"); }
			catch { this.ConnectionString = string.Empty; }
		}
		#endregion

		#region Properties

		#region Initialization
		/// <summary>
		/// Gets or sets the friendly name for this instance.
		/// </summary>
		[Category("Initialization")]
		[Description("The unique friendly name for this Database Connection")]
		[ParenthesizePropertyName]
		[ReadOnly(true)]
		public string Name
		{
			get;
			set;
		}
		/// <summary>
		/// Gets or sets the Command Timeout
		/// </summary>
		[Category("Initialization")]
		[DisplayName("Command Timeout")]
		[Description("The default Command Timeout for a SqlCommand object.")]
		[ReadOnly(true)]
		public int CommandTimeout
		{
			get;
			set;
		}
		/// <summary>
		/// Gets or sets the Connect Timeout
		/// </summary>
		[Category("Initialization")]
		[DisplayName("Connect Timeout")]
		[Description("The length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error.")]
		[ReadOnly(true)]
		public int ConnectTimeout
		{
			get { return this.builder.ConnectTimeout; }
			set { this.builder.ConnectTimeout = value; }
		}
		#endregion

		#region Security
		/// <summary>
		/// Gets or sets a Boolean value that indicates whether User ID and Password are specified in the 
		/// connection (when false) or whether the current Windows account credentials are used for 
		/// authentication (when true).
		/// </summary>
		/// <returns>The value of the IntegratedSecurity property, or false if none has been supplied.</returns>
		[Category("Security")]
		[DisplayName("Integrated Security")]
		[Description("Whether the connection is to be a secure connection or not.")]
		[ReadOnly(true)]
		public bool IntegratedSecurity
		{
			get
			{
				return this.builder.IntegratedSecurity;
			}
			set
			{
				this.builder.IntegratedSecurity = value;
			}
		}
		/// <summary>
		/// Gets or sets the user ID to be used when connecting to Server.
		/// </summary>
		/// <returns>The value of the UserID property, or String.Empty if none has been supplied.</returns>
		[Category("Security")]
		[DisplayName("User ID")]
		[Description("Indicates the user ID to be used when connecting to the data source.")]
		[ReadOnly(true)]
		public string UserID
		{
			get { return this.builder.UserID; }
			set { this.builder.UserID = value; }
		}
		/// <summary>
		/// Gets or sets the password for the Server account.
		/// </summary>
		/// <returns>The value of the Password property, or String.Empty if none has been supplied.</returns>
		[Category("Security")]
		[DisplayName("Password")]
		[Description("Indicates the password to be used when connecting to the data source.")]
		[PasswordPropertyText(true)]
		[ReadOnly(true)]
		public string Password
		{
			get { return this.builder.Password; }
			set { this.builder.Password = value; }
		}
		#endregion

		#region Source
		/// <summary>
		/// Gets or sets the name or network address of the instance of Server to connect to.
		/// </summary>
		/// <returns>The value of the DataSource property, or String.Empty if none has been supplied.</returns>
		[Category("Source")]
		[DisplayName("Data Source")]
		[Description("Indicates the name of the data source to connect to.")]
		[ReadOnly(true)]
		public string DataSource
		{
			get { return this.builder.DataSource; }
			set { this.builder.DataSource = value; }
		}
		/// <summary>
		/// Gets or sets the name of the database associated with the connection.
		/// </summary>
		/// <returns>The value of the InitialCatalog property, or String.Empty if none has been supplied.</returns>
		[Category("Source")]
		[DisplayName("Initial Catalog")]
		[Description("The name of the initial catalog or database in the data source.")]
		[ReadOnly(true)]
		public string InitialCatalog
		{
			get { return this.builder.InitialCatalog; }
			set { this.builder.InitialCatalog = value; }
		}
		#endregion

		#region Context
		/// <summary>
		/// Gets or sets the name of the application associated with the connection string.
		/// </summary>
		/// <returns>The name of the application.</returns>
		[Category("Context")]
		[DisplayName("Application Name")]
		[Description("The name of the application connecting to the SQL Server.")]
		[ReadOnly(true)]
		public string ApplicationName
		{
			get { return this.builder.ApplicationName; }
			set { this.builder.ApplicationName = value; }
		}
		/// <summary>
		/// Gets or sets the name of the workstation connecting to Server.
		/// </summary>
		/// <returns>The value of the WorkstationID property, or String.Empty if none has been supplied.</returns>
		[Category("Context")]
		[DisplayName("Workstation ID")]
		[Description("The name of the workstation connecting to the SQL Server.")]
		[ReadOnly(true)]
		public string WorkstationID
		{
			get { return this.builder.WorkstationID; }
			set { this.builder.WorkstationID = value; }
		}
		#endregion

		#region Non-Browsable
		/// <summary>
		/// Gets or sets the connection string.
		/// </summary>
		[Browsable(false)]
		public string ConnectionString
		{
			get { return this.builder.ConnectionString; }
			set { this.builder.ConnectionString = value; }
		}
		/// <summary>
		/// Gets the current connection string masking the Password.
		/// </summary>
		[Browsable(false)]
		public string ConnectionStringMasked
		{
			get
			{
				var cnString = this.ConnectionString;

				if (!this.IntegratedSecurity)
				{
					var scs = new SqlConnectionStringBuilder(cnString);
					if (!string.IsNullOrEmpty(scs.Password.Trim()))
						scs.Password = new string(PwdChar, 8);
					cnString = scs.ToString();
				}

				return cnString;
			}
		}
		#endregion

		#endregion

		#region Overrides
		/// <summary>
		/// Gets the Name of this DataAccessConnection Instance
		/// </summary>
		/// <returns>Returns the Name of this DataAccessConnection Instance</returns>
		public override string ToString()
		{
			return this.Name;
		}
		#endregion

		#region ISerializable
		/// <summary>
		/// Serializes this instance.
		/// </summary>
		/// <param name="info">The SerializationInfo container.</param>
		/// <param name="context">Not used.</param>
		public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			Guard.ArgumentNotNull(info, "info");
			info.AddValue("Name", this.Name);
			info.AddValue("CommandTimeout", this.CommandTimeout);
			info.AddValue("ConnectionString", this.ConnectionString);
		}
		#endregion
	}
	#endregion
}